using System;
using System.Diagnostics;
using Rhizome.Contract;
using Rhizome.Internal;

namespace Rhizome.Model
{
	public sealed class RhizomeProperty<T> : IIdentityAccessor<string>
	{
		private static readonly IdKeyedCollection<string, RhizomeProperty<T>> Properties =
			new IdKeyedCollection<string, RhizomeProperty<T>>();

		private readonly Func<T> _getDefaultValue;


		private T _defaultValue;
		private bool _defaultValueInited;

		private RhizomeProperty(string propertyName, bool inheritable, T defaultValue)
		{
			if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("can not be null or empty", "propertyName");

			Id = propertyName;
			DefaultValue = defaultValue;
			Inheritable = inheritable;
		}

		private RhizomeProperty(string propertyName, bool inheritable, Func<T> getDefaultValue)
		{
			if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("can not be null or empty", "propertyName");
			if (getDefaultValue == null) throw new ArgumentNullException("getDefaultValue");

			Id = propertyName;
			_getDefaultValue = getDefaultValue;
			Inheritable = inheritable;
		}

		public T DefaultValue
		{
			get
			{
				lock (this)
				{
					if (_defaultValueInited == false) 
						DefaultValue = _getDefaultValue();
					
					return _defaultValue;
				}
			}
			[DebuggerStepThrough]
			private set
			{
				lock (this)
				{
					_defaultValue = value;
					_defaultValueInited = true;
				}
			}
		}

		public bool Inheritable { get; private set; }

		#region IIdentityAccessor<string> Members

		public string Id { get; private set; }

		#endregion

		public static RhizomeProperty<T> Register(string propertyName, bool inheritable, T defaultValue)
		{
			var property = new RhizomeProperty<T>(propertyName, inheritable, defaultValue);
			Properties.Add(property);
			return property;
		}

		public static RhizomeProperty<T> Register(string propertyName, bool inheritable, Func<T> getDefaultValue)
		{
			var property = new RhizomeProperty<T>(propertyName, inheritable, getDefaultValue);
			Properties.Add(property);
			return property;
		}

		[DebuggerStepThrough]
		public override string ToString()
		{
			return Id;
		}

		public bool Equals(RhizomeProperty<T> other)
		{
			if (ReferenceEquals(null, other)) return false;
			if (ReferenceEquals(this, other)) return true;
			return Equals(other.Id, Id);
		}

		public override bool Equals(object obj)
		{
			if (ReferenceEquals(null, obj)) return false;
			if (ReferenceEquals(this, obj)) return true;
			if (obj.GetType() != typeof (RhizomeProperty<T>)) return false;
			return Equals((RhizomeProperty<T>) obj);
		}

		public override int GetHashCode()
		{
			return Id.GetHashCode();
		}

		public static bool operator ==(RhizomeProperty<T> left, RhizomeProperty<T> right)
		{
			return Equals(left, right);
		}

		public static bool operator !=(RhizomeProperty<T> left, RhizomeProperty<T> right)
		{
			return !Equals(left, right);
		}
	}
}